package com.emeraldparser.util;

import static com.emeraldparser.util.Collections3.set;
import static com.google.common.collect.Collections2.filter;
import static com.google.common.collect.Collections2.transform;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.collect.ForwardingCollection;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Table;
import com.google.common.collect.Table.Cell;

public abstract class AbstractMultitableOld<R, C, V> implements Multitable<R, C, V> {

	@Override
	public abstract Collection<Cell<R, C, V>> cells();

	@Override
	public boolean contains(Object rowKey, Object columnKey) {
		return Iterables.any(cells(), cellPredicate(rowKey, columnKey));
	}

	@Override
	public boolean containsColumn(Object columnKey) {
		return transform(cells(), columnKeyFunction()).contains(columnKey);
	}

	@Override
	public boolean containsRow(Object rowKey) {
		return transform(cells(), rowKeyFunction()).contains(rowKey);
	}

	@Override
	public boolean containsValue(Object value) {
		return transform(cells(), valueFunction()).contains(value);
	}

	@Override
	public Collection<V> get(final R rowKey, final C columnKey) {
		final Predicate<Cell<?, ?, ?>> cellPredicate = cellPredicate(rowKey, columnKey);
		
		return new ForwardingCollection<V>() {

			@Override
			protected Collection<V> delegate() {
				return transform(filter(cells(), cellPredicate), valueFunction());
			}

			@Override
			public boolean add(V value) {
				return cells().add(ImmutableTableCell.create(rowKey, columnKey, value));
			}

			@Override
			public boolean addAll(Collection<? extends V> collection) {
				return standardAddAll(collection);
			}

		};
	}

	private Function<Cell<? extends R, ?, ?>, R> rowKeyFunction() {
		return new Function<Cell<? extends R, ?, ?>, R>() {
			@Override
			public R apply(Cell<? extends R, ?, ?> cell) {
				return cell.getRowKey();
			}
		};
	}

	private Function<Cell<?, ? extends C, ?>, C> columnKeyFunction() {
		return new Function<Cell<?, ? extends C, ?>, C>() {
			@Override
			public C apply(Cell<?, ? extends C, ?> cell) {
				return cell.getColumnKey();
			}
		};
	}

	private Function<Cell<?, ?, ? extends V>, V> valueFunction() {
		return new Function<Cell<?, ?, ? extends V>, V>() {
			@Override
			public V apply(Cell<?, ?, ? extends V> cell) {
				return cell.getValue();
			}
		};
	}

	@Override
	public boolean isEmpty() {
		return cells().isEmpty();
	}

	@Override
	public int size() {
		return cells().size();
	}

	@Override
	public void clear() {
		cells().clear();
	}

	/**
	 * TODO Determine what the return type should be
	 * 
	 * @param rowKey
	 * @param columnKey
	 * @param value
	 */
	@Override
	public boolean put(R rowKey, C columnKey, V value) {
		return cells().add(ImmutableTableCell.create(rowKey, columnKey, value));
	}

	@Override
	public boolean putAll(Multitable<? extends R, ? extends C, ? extends V> multitable) {
		boolean changed = false;
		for (Cell<? extends R, ? extends C, ? extends V> cell : multitable.cells()) {
			changed |= cells().add(ImmutableTableCell.<R, C, V>create(cell.getRowKey(), cell.getColumnKey(), cell.getValue()));
		}
		return changed;
	}

	@Override
	public boolean putAll(R rowKey, C columnKey, Iterable<? extends V> values) {
		boolean changed = false;
		for (V value : values) {
			changed |= cells().add(ImmutableTableCell.<R, C, V>create(rowKey, columnKey, value));
		}
		return changed;
	}

	@Override
	public Collection<V> removeAll(Object rowKey, Object columnKey) {
		List<V> removed = Lists.newLinkedList();
		
		Collection<V> values = transform(filter(cells(), cellPredicate(rowKey, columnKey)), valueFunction());
		
		for (Iterator<V> i = values.iterator(); i.hasNext();) {
			removed.add(i.next());
			i.remove();
		}
		
		return removed;
	}

	private Predicate<Cell<?, ?, ?>> cellPredicate(final Object rowKey, final Object columnKey) {
		return new Predicate<Cell<?, ?, ?>>() {
			@Override
			public boolean apply(Cell<?, ?, ?> cell) {
				return Objects.equal(rowKey, cell.getRowKey())
				    && Objects.equal(columnKey, cell.getColumnKey());
			}
		};
	}

	@Override
	public Multimap<C, V> row(final R rowKey) {
		final Predicate<Cell<R, C, V>> cellPredicate = new Predicate<Cell<R, C, V>>() {
			public boolean apply(Cell<R, C, V> cell) {
				return Objects.equal(rowKey, cell.getRowKey());
			}
		};
		
		return new CollectionBasedMultimap<Cell<R, C, V>, C, V>() {

			@Override
			protected Collection<Cell<R, C, V>> collection() {
				return filter(cells(), cellPredicate);
			}

			@Override
			protected C key(Cell<R, C, V> cell) {
				return cell.getColumnKey();
			}

			@Override
			protected V value(Cell<R, C, V> cell) {
				return cell.getValue();
			}

			@Override
			protected Cell<R, C, V> element(C columnKey, V value) {
				return ImmutableTableCell.create(rowKey, columnKey, value);
			}

		};
	}

	@Override
	public Multimap<R, V> column(final C columnKey) {
		final Predicate<Cell<R, C, V>> cellPredicate = new Predicate<Cell<R, C, V>>() {
			public boolean apply(Cell<R, C, V> cell) {
				return Objects.equal(columnKey, cell.getColumnKey());
			}
		};
		
		return new CollectionBasedMultimap<Cell<R, C, V>, R, V>() {

			@Override
			protected Collection<Cell<R, C, V>> collection() {
				return filter(cells(), cellPredicate);
			}

			@Override
			protected R key(Cell<R, C, V> cell) {
				return cell.getRowKey();
			}

			@Override
			protected V value(Cell<R, C, V> cell) {
				return cell.getValue();
			}

			@Override
			protected Cell<R, C, V> element(R rowKey, V value) {
				return ImmutableTableCell.create(rowKey, columnKey, value);
			}

		};
	}

	@Override
	public Set<R> rowKeySet() {
		return set(transform(cells(), rowKeyFunction()));
	}

	@Override
	public Set<C> columnKeySet() {
		return set(transform(cells(), columnKeyFunction()));
	}

	@Override
	public Collection<V> values() {
		return transform(cells(), valueFunction());
	}

	protected R castRowKey(Object object) {
		return (R) object;
	}

	protected C castColumnKey(Object object) {
		return (C) object;
	}

	@Override
	public Map<R, Multimap<C, V>> rowMap() {
		return new CollectionBasedMap<Cell<R, C, V>, R, Multimap<C, V>>() {

			@Override
			protected Collection<Cell<R, C, V>> collection() {
				return cells();
			}

			@Override
			protected R key(Cell<R, C, V> cell) {
				return cell.getRowKey();
			}

			@Override
			protected Iterable<? extends Cell<R, C, V>> elements(final R rowKey, Multimap<C, V> column) {
				return transform(
						column.entries(),
						new Function<Map.Entry<C, V>, Cell<R, C, V>>() {
							@Override
							public Cell<R, C, V> apply(Map.Entry<C, V> entry) {
								return ImmutableTableCell.create(rowKey, entry.getKey(), entry.getValue());
							}
						}
					);
			}

			@Override
			protected Multimap<C, V> value(Object key, final Collection<Cell<R, C, V>> filteredElements) {
				final R rowKey = castRowKey(key);
				
				return new CollectionBasedMultimap<Cell<R, C, V>, C, V>() {

					@Override
					protected Collection<Cell<R, C, V>> collection() {
						return filteredElements;
					}

					@Override
					protected C key(Cell<R, C, V> cell) {
						return cell.getColumnKey();
					}

					@Override
					protected V value(Cell<R, C, V> cell) {
						return cell.getValue();
					}

					@Override
					protected Cell<R, C, V> element(C columnKey, V value) {
						return ImmutableTableCell.create(rowKey, columnKey, value);
					}

				};
			}

		};
	}

	@Override
	public Map<C, Multimap<R, V>> columnMap() {
		return new CollectionBasedMap<Cell<R, C, V>, C, Multimap<R, V>>() {

			@Override
			protected Collection<Cell<R, C, V>> collection() {
				return cells();
			}

			@Override
			protected C key(Cell<R, C, V> cell) {
				return cell.getColumnKey();
			}

			@Override
			protected Iterable<? extends Cell<R, C, V>> elements(final C columnKey, Multimap<R, V> row) {
				return transform(
						row.entries(),
						new Function<Map.Entry<R, V>, Cell<R, C, V>>() {
							@Override
							public Cell<R, C, V> apply(Map.Entry<R, V> entry) {
								return ImmutableTableCell.create(entry.getKey(), columnKey, entry.getValue());
							}
						}
					);
			}

			@Override
			protected Multimap<R, V> value(Object key, final Collection<Cell<R, C, V>> filteredElements) {
				final C columnKey = castColumnKey(key);
				
				return new CollectionBasedMultimap<Cell<R, C, V>, R, V>() {

					@Override
					protected Collection<Cell<R, C, V>> collection() {
						return filteredElements;
					}

					@Override
					protected R key(Cell<R, C, V> cell) {
						return cell.getRowKey();
					}

					@Override
					protected V value(Cell<R, C, V> cell) {
						return cell.getValue();
					}

					@Override
					protected Cell<R, C, V> element(R rowKey, V value) {
						return ImmutableTableCell.create(rowKey, columnKey, value);
					}

				};
			}

		};
	}

	@Override
	public int hashCode() {
		return asTable().hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == this) {
			return true;
		}
		
		if (obj == null) {
			return false;
		}
		
		if (!(obj instanceof Multitable<?, ?, ?>)) {
			return false;
		}
		
		Multitable<?, ?, ?> other = (Multitable<?, ?, ?>) obj;
		
		return asTable().equals(other.asTable());
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		
		builder.append("{");
		
		String rowDelimiter = "";
		for (Map.Entry<R, Multimap<C, V>> rowEntry : rowMap().entrySet()) {
			builder.append(rowDelimiter).append(rowEntry.getKey());
			builder.append("=");
			builder.append("{");
			
			String columnDelimiter = "";
			for (Map.Entry<C, V> columnEntry : rowEntry.getValue().entries()) {
				builder.append(columnDelimiter).append(columnEntry.getKey()).append("=").append(columnEntry.getValue());
				columnDelimiter = ", ";
			}
			
			builder.append("}");
			rowDelimiter = ", ";
		}
		
		builder.append("}");
		
		return builder.toString();
	}

	@Override
	public Table<R, C, Collection<V>> asTable() {
		// TODO Auto-generated method stub
		return new CollectionBasedTable<Cell<R, C, V>, R, C, Collection<V>>() {

			@Override
			protected Collection<Cell<R, C, V>> collection() {
				// TODO Auto-generated method stub
				return AbstractMultitableOld.this.cells();
			}

			@Override
			public Set<Cell<R, C, Collection<V>>> cellSet() {
				// TODO Auto-generated method stub
				return null;
			}

			@Override
			protected R rowKey(Cell<R, C, V> element) {
				return element.getRowKey();
			}

			@Override
			protected C columnKey(Cell<R, C, V> element) {
				return element.getColumnKey();
			}

			@Override
			protected Collection<V> value(Cell<R, C, V> element) {
				// TODO Auto-generated method stub
				return null;
			}

			@Override
			protected Collection<V> value(Object rowKey, Object columnKey, Collection<Cell<R, C, V>> filteredElements) {
				// TODO Auto-generated method stub
				return transform(filteredElements, valueFunction());
			}

			@Override
			protected Collection<com.google.common.collect.Table.Cell<R, C, Collection<V>>> cells() {
				// TODO Auto-generated method stub
				return null;
			}

		};
	}

}
